package com.example.wangliang.piechartview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;


import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 该自定义控件中的文本可以在9个方位进行控制
 * 左上——中上——右上
 * 左中——中中——右中
 * 左下——中下——右下
 * 创建时间：2016.7.22
 * 创建人：王亮
 * 功能作用：百分比饼图，同时显示文字，以及非百分比文字
 */
public class PieChartView extends View {

    private TimeControlChartAnimation anim;//动画计时器
    private int ANIMATION_TIME = 1500;//动画的显示时间
    private RectF rect;//按照所设置的宽高中的最小值来设置范围
    private Paint paint;//默认画笔
    public static final String KEY_NAME = "key_name";//向控件传递名称的集合的键
    public static final String KEY_PERCENT = "key_percent";//向控件传递百分比的集合的键
    public static final String KEY_COLOR = "key_color";//向控件传递颜色的集合的键
    private List<Map<String,Object>> list;//存储百分比显示的所有数据，名称，百分比，颜色
    private int  rotationAngle = 0;//动画显示中正在旋转的角度
    private int  oldRotationAngle = 0;//动画显示中已经显示完的百分比模块的百分比总和所占的角度，也是，正在显示模块的起点
    private int  maxRotationAngle = 360;//最大的旋转角度
    private boolean isShowAnim = false;//是否显示动画
    private Float strokeWidth;//空心圆半径
    private int minMargin;//最短的控件间距

    //除了百分比图片上的文字之外的文字
    /** 要显示的文字 */
    private String showText;
    /** 文字的颜色 */
    private int showTextColor;
    /** 文字的大小 */
    private int showTextSize;
    /** 文字的方位 */
    private int showTextAlign;
    /** 左上角显示  */
    public static final int TEXT_ALIGN_LEFT_TOP                 = 0x00000001;
    /** 左侧中间显示  */
    public static final int TEXT_ALIGN_LEFT_CENTER              = 0x00000010;
    /** 左侧下方显示  */
    public static final int TEXT_ALIGN_LEFT_BOTTOM              = 0x00000100;
    /** 右上角显示  */
    public static final int TEXT_ALIGN_RIGHT_TOP                = 0x00001000;
    /** 右侧中间显示 */
    public static final int TEXT_ALIGN_RIGHT_CENTER             = 0x00010000;
    /** 右侧下方显示  */
    public static final int TEXT_ALIGN_RIGHT_BOTTOM             = 0x00100000;
    /** 中间顶部显示  */
    public static final int TEXT_ALIGN_CENTER_HORIZONTAL_TOP    = 0x00001000|0x00010000;
    /** 中间下方显示  */
    public static final int TEXT_ALIGN_CENTER_HORIZONTAL_BOTTOM = 0x10000010;
    /** 居中显示  */
    public static final int TEXT_ALIGN_CENTER                   = 0x10000100;
    /** 居中靠上显示  */
    public static final int TEXT_ALIGN_CENTER_TOP               = 0x10001000;
    /** 居中靠下显示  */
    public static final int TEXT_ALIGN_CENTER_BOTTOM            = 0x10010000;
    /** 居中靠左显示  */
    public static final int TEXT_ALIGN_CENTER_LEFT              = 0x10100000;
    /** 居中靠右显示  */
    public static final int TEXT_ALIGN_CENTER_RIGHT             = 0x11000000;
    /** 居中靠左上显示  */
    public static final int TEXT_ALIGN_CENTER_LEFT_TOP          = 0x11000001;
    /** 居中靠左下显示  */
    public static final int TEXT_ALIGN_CENTER_LEFT_BOTTOM       = 0x11000010;
    /** 居中靠右上显示  */
    public static final int TEXT_ALIGN_CENTER_RIGHT_TOP         = 0x11000100;
    /** 居中靠右下显示  */
    public static final int TEXT_ALIGN_CENTER_RIGHT_BOTTOM      = 0x11001000;
    /**   */
    /** 文本中轴线X坐标 */
    private Float showTextCenterX;
    /** 文本baseline线Y坐标 */
    private Float showTextBaselineY;
    /** 控件的宽度 */
    private int viewWidth;
    /** 控件的高度 */
    private int viewHeight;
    private Paint.FontMetrics fm;
    /** 场景 */
    private Context context;
    private Paint showTextPaint;
    //除了百分比图片上的文字之外的文字

    private Bitmap backgroundBitmap;//饼状图背景图片
    private XChartCalc xChartCalcPercentText;//百分比文字显示位置计算类


    public PieChartView(Context context) {
        super(context);
        initViewSetting();
    }

    public PieChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initViewSetting();
    }

    public PieChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initViewSetting();
    }
    /**
     * 初始化设置
     */
    private void initViewSetting(){
        anim = new TimeControlChartAnimation(this, new TimeControlChartAnimation.SetPercentCallbackListener() {
            @Override
            public void percent(float interpolatedTime) {
                Log.e("interpolatedTime", String.valueOf(Double.valueOf(interpolatedTime)));
                if(interpolatedTime <= 1 && interpolatedTime >= 0){//interpolatedTime：根据动画的显示时间返回0-1之间的值，未显示为0，显示完全为1
                    rotationAngle = (int) (interpolatedTime * maxRotationAngle);
                }
            }
        });//初始化动画计时器
        anim.setDuration(ANIMATION_TIME);//设置动画显示时间
        paint = new Paint(); //设置一个笔刷大小是3的黄色的画笔
        paint.setColor(Color.BLACK);    //设置画笔颜色
        paint.setAntiAlias(true);  //是否抗锯齿
        paint.setTextAlign(Paint.Align.CENTER);//设置文字对齐方式，取值：align.CENTER、align.LEFT或align.RIGHT
        paint.setTextSize(20);//设置文字大小//样式设置
//        paint.setFakeBoldText(true);//设置是否为粗体文字
//        paint.setUnderlineText(true);//设置下划线
//        paint.setTextSkewX((float) -0.25);//设置字体水平倾斜度，普通斜体字是-0.25
//        paint.setStrikeThruText(true);//设置带有删除线效果//其它设置
        paint.setTextScaleX(0);//只会将水平方向拉伸，高度不会变

        showTextPaint = new Paint();
        showTextPaint.setAntiAlias(true);
        showTextPaint.setTextAlign(Paint.Align.CENTER);
        //默认情况下文字居中显示
        showTextAlign = TEXT_ALIGN_CENTER;
        //默认的文本颜色是黑色
        this.showTextColor = Color.BLACK;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        viewWidth = getWidth();
        viewHeight = getWidth();
        super.onLayout(changed, left, top, right, bottom);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = getDefaultSize(getSuggestedMinimumHeight(),
                heightMeasureSpec);
        int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        minMargin = Math.min(width, height);//取边框的最小值来显示图表
        setMeasuredDimension(minMargin, minMargin);
        rect = new RectF(0,0,minMargin,minMargin);//按照所设置的宽高中的最小值来设置范围
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if(backgroundBitmap == null) {
            //显示是百分比饼图，动画以及非动画
            showPercentCircle(canvas);
        }else {
            canvas.drawBitmap(getCroppedRoundBitmap(backgroundBitmap,getWidth()/2),0,0,null);
        }

        //画空心圆
        if(strokeWidth != null) {
            Paint paintStroke = new Paint();
            paintStroke.setColor(Color.WHITE);
            canvas.drawCircle(getWidth() / 2 , getHeight() / 2 ,strokeWidth * minMargin, paintStroke);
        }

        //绘制除百分比以外文字
        if(showText != null){
            if(showTextCenterX == null || showTextBaselineY == null) {
                showTextPaint.setTextSize(showTextSize);
                showTextPaint.setColor(showTextColor);
                setTextLocation();
            }
            if(showTextCenterX != null && showTextBaselineY != null) {
                showTextPaint.setTextSize(showTextSize);
                showTextPaint.setColor(showTextColor);
                canvas.drawText(showText, showTextCenterX, showTextBaselineY, showTextPaint);
            }
        }
    }

    /**
     * //显示是百分比饼图，动画以及非动画
     * @param canvas
     */
    private void showPercentCircle(Canvas canvas){
        if(isShowAnim){
            for(int i = 0 ; i < list.size() ; i++){
                paint.setColor((int) list.get(i).get(KEY_COLOR));    //设置画笔颜色
                int percent = 0;
                if( i == 0) {//0的时候单独设置，因为需要从0开始
                    canvas.drawArc(rect, 0, (float) (rotationAngle * (int) list.get(i).get(KEY_PERCENT) * 1.0 /100), true, paint);
                }else {
                    for (int j = 0; j < i; j++) {
                        percent += (int) list.get(j).get(KEY_PERCENT);
                    }
                    percent = (int) (percent * 1.0 / 100 * maxRotationAngle);//计算已经显示过的百分比所旋转的角度
                    canvas.drawArc(rect,percent,
                            (float) (rotationAngle * (int) list.get(i).get(KEY_PERCENT) * 1.0 /100 + 1)
                            ,true,paint);//非零状态下的动画绘制需要从该条的起点开始绘制，每次绘制需要加1，防止四舍五入产生误差
                }
            }
        }else {
            oldRotationAngle = 0;
            for(int i = 0 ; i < (list != null ? list.size() : 0) ; i++){//(list != null ? list.size() : 0)
                paint.setColor((int)list.get(i).get(KEY_COLOR));    //设置画笔颜色
                rotationAngle = (int) ((int)list.get(i).get(KEY_PERCENT) * 1.0 / 100 * maxRotationAngle);
                canvas.drawArc(rect,oldRotationAngle, rotationAngle, true, paint);
                oldRotationAngle = oldRotationAngle + rotationAngle;
            }
        }

        if(rotationAngle == maxRotationAngle || !isShowAnim){
            for(int j = 0 ; j < (list != null ? list.size() : 0) ; j++){
                int percent = 0;
                for (int k = 0; k < j; k++) {
                    percent += (int) list.get(k).get(KEY_PERCENT);
                }
                percent = (int) (percent * 1.0 / 100 * maxRotationAngle);
                paint.setColor(Color.GREEN);    //设置画笔颜色
                if(minMargin != 0 ) {
                    xChartCalcPercentText = new XChartCalc();
                    xChartCalcPercentText.CalcArcEndPointXY(minMargin / 2 , minMargin / 2);
                    canvas.drawText((String) list.get(j).get(KEY_NAME)
                            ,xChartCalcPercentText.setCirAngle(minMargin / 2 - minMargin / 6,percent + (float) ((int) list.get(j).get(KEY_PERCENT) * 0.5 /100) * maxRotationAngle)
                                    .getPosX()
                            ,xChartCalcPercentText.setCirAngle(minMargin / 2 - minMargin / 6,percent + (float) ((int) list.get(j).get(KEY_PERCENT) * 0.5 /100) * maxRotationAngle)
                                    .getPosY()
                            ,paint);//文字在扇形的中间线上的某一点显示
                }
            }
        }
    }

    /**
     * 定位文本绘制的位置
     */
    private void setTextLocation() {
        fm = showTextPaint.getFontMetrics();
        //文本的宽度
        float textWidth = showTextPaint.measureText(showText);
        float textHeight = fm.descent - fm.ascent;
        float textCenterVerticalBaselineY = viewHeight / 2 - fm.descent + (fm.descent - fm.ascent) / 2;
        switch (showTextAlign) {
            case TEXT_ALIGN_CENTER://居中
                showTextCenterX = (float)viewWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_LEFT_CENTER://居中靠最左
                showTextCenterX = textWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_RIGHT_CENTER://居中靠最右
                showTextCenterX = viewWidth - textWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_HORIZONTAL_BOTTOM://居中靠最下
                showTextCenterX = Float.valueOf(viewWidth / 2);
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_CENTER_HORIZONTAL_TOP://居中靠最上
                showTextCenterX = Float.valueOf(viewWidth / 2);
                showTextBaselineY = -fm.ascent;
                break;
            case TEXT_ALIGN_LEFT_TOP://左上
                showTextCenterX = textWidth / 2;
                showTextBaselineY =  -fm.ascent;
                break;
            case TEXT_ALIGN_LEFT_BOTTOM://左下
                showTextCenterX = textWidth / 2;//
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_RIGHT_TOP://右上
                showTextCenterX = viewWidth - textWidth / 2;
                showTextBaselineY = -fm.ascent;
                break;
            case TEXT_ALIGN_RIGHT_BOTTOM://右下
                showTextCenterX = viewWidth - textWidth / 2;
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_CENTER_TOP://居中靠上
                showTextCenterX = (float)viewWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight / 2;
                break;
            case TEXT_ALIGN_CENTER_BOTTOM:/** 居中靠下显示  */
                showTextCenterX = (float)viewWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight / 2;
                break;
            case TEXT_ALIGN_CENTER_LEFT:/** 居中靠左显示  */
                showTextCenterX = (float)viewWidth / 2 - textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_RIGHT:/** 居中靠右显示  */
                showTextCenterX = (float)viewWidth / 2 + textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_LEFT_TOP:/** 居中靠左上显示  */
                showTextCenterX = (float)viewWidth / 2 - textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight / 2 ;
                break;
            case TEXT_ALIGN_CENTER_LEFT_BOTTOM:/** 居中靠左下显示  */
                showTextCenterX = (float)viewWidth / 2 - textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight / 2 ;
                break;
            case TEXT_ALIGN_CENTER_RIGHT_TOP:/** 居中靠右上显示  */
                showTextCenterX = (float)viewWidth / 2 + textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight / 2 ;
                break;
            case TEXT_ALIGN_CENTER_RIGHT_BOTTOM:/** 居中靠右下显示  */
                showTextCenterX = (float)viewWidth / 2 + textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight / 2 ;
                break;
        }
    }

    /**
     *  设置要显示的内容，主要是，名称颜色以及百分比,此时的百分比是10的整数倍形式
     * @param list
     */
    public void setShowList(List<Map<String,Object>> list,Boolean isShowAnim){
        int percent = 0;
        for(int i = 0 ; i < list.size() ; i++){
            percent += (int) list.get(i).get(KEY_PERCENT);
        }
        if(percent > 100){
            try {
                throw new Exception();
            }catch (Exception e){
                Log.e(getClass().getName(),"百分比总和不能大于100");
                return;
            }
        }else if(percent < 100){
            HashMap<String, Object> map = new HashMap<>();
            map.put(KEY_NAME,"");
            map.put(KEY_PERCENT,100 - percent);
            map.put(KEY_COLOR, Color.WHITE);
            list.add(map);
        }//判断一下当前传递的百分比的总和是否大于100，大于则抛出异常，否则继续判断是否小于100，小于100的部分则用白色补齐，是因为背景不一定会为非白色背景
        this.list = list;
        this.isShowAnim = (isShowAnim == null) || isShowAnim == false ? false : true;//进行是否显示动画的初始化
        rotationAngle = 0;//动画显示中正在旋转的角度
        oldRotationAngle = 0;
        postInvalidate();
        initViewSetting();
        if(isShowAnim != null && isShowAnim){
            startAnimation(anim);
        }
    }

    /**
     * 设置饼状图空心以及空心大小
     * @param strokeWidth 空心圆半径是父背景半径的百分比
     */
    public void setStrokeWidth(Float strokeWidth){
        this.strokeWidth = (strokeWidth == null) ? 0 : strokeWidth / 2;
        if(strokeWidth > 1){
            try {
                throw new Exception();
            }catch (Exception e){
                Log.e(getClass().getName(),"百分比不能大于1");
            }
        }
        postInvalidate();
    }

    /**
     * 设置文字以及中心文字大小
     * @param showText 文字
     * @param showTextColor 文字颜色
     * @param showTextSize 文字大小
     * @param showTextAlign 文字方位
     */
    public void setShowText(String showText , Integer showTextColor ,Integer showTextSize,Integer showTextAlign){
        this.showText = showText;
        this.showTextColor = showTextColor;
        this.showTextSize = showTextSize;
        this.showTextAlign = showTextAlign;
        postInvalidate();
    }

    /**
     * 设置饼状图背景图片，注意，如果设置饼状图背景图片则百分比的饼状图失效
     * @param backgroundBitmap
     */
    public void setBackgroundImage(Bitmap backgroundBitmap){
        this.backgroundBitmap = backgroundBitmap;
        postInvalidate();
    }

    /**
     * 获取裁剪后的圆形图片
     *
     * @param radius
     *            半径
     */
    private Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {
        Bitmap scaledSrcBmp;
        int diameter = radius * 2;

        // 为了防止宽高不相等，造成圆形图片变形，因此截取长方形中处于中间位置最大的正方形图片
        int bmpWidth = bmp.getWidth();
        int bmpHeight = bmp.getHeight();
        int squareWidth = 0, squareHeight = 0;
        int x = 0, y = 0;
        Bitmap squareBitmap;
        if (bmpHeight > bmpWidth) {// 高大于宽
            squareWidth = squareHeight = bmpWidth;
            x = 0;
            y = (bmpHeight - bmpWidth) / 2;
            // 截取正方形图片
            squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
                    squareHeight);
        } else if (bmpHeight < bmpWidth) {// 宽大于高
            squareWidth = squareHeight = bmpHeight;
            x = (bmpWidth - bmpHeight) / 2;
            y = 0;
            squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
                    squareHeight);
        } else {
            squareBitmap = bmp;
        }

        if (squareBitmap.getWidth() != diameter
                || squareBitmap.getHeight() != diameter) {
            scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter,
                    diameter, true);

        } else {
            scaledSrcBmp = squareBitmap;
        }
        Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),
                scaledSrcBmp.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        Paint paint = new Paint();
        Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(),
                scaledSrcBmp.getHeight());

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawCircle(scaledSrcBmp.getWidth() / 2,
                scaledSrcBmp.getHeight() / 2, scaledSrcBmp.getWidth() / 2,
                paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
        // bitmap回收(recycle导致在布局文件XML看不到效果)
        // bmp.recycle();
        // squareBitmap.recycle();
        // scaledSrcBmp.recycle();
        bmp = null;
        squareBitmap = null;
        scaledSrcBmp = null;
        return output;
    }

}
